//Version 15 - Parte A y B:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#define STDIN_FID 0
#define STDOUT_FID 1

int parsing(char line[], char* args[], char type1[], char type2[]);
void command(char* args[], char* path);
void seek(char *arreglo[]);
void changeDir(char* args[], char* path);
void showPrompt();
int whichstatus(int cantArgs,char* args[]);
void redirection(char* file_name);

char* directories[100];
char* PATH;
int flagstatus;
int position;

int main(int argc, char *argv[]){
    printf("--------------------------BAASH--------------------------\n");
    char* args[100];   
    char line[1024];
    int cantarg;
    
    PATH = getenv("PATH");
    parsing(PATH, directories, ":",":");
        
    while(1){  
        
        flagstatus=0;
        showPrompt();                  
        
        if (fgets(line, 1024, stdin) == NULL) return 0;
		else{
			cantarg=parsing(line, args, " ", " \n");    //Parsea la linea de entrada.
			position=whichstatus(cantarg,args);
			command(args, getcwd(NULL, 0));     //sino, interpreta el comando (agrs[0])
			}
		

    }
    return 0;
}

int whichstatus(int cantArgs,char* args[]){
	int i;
	for (i=0;i<cantArgs;i++){
		if(strcmp(args[i],"&")==0){
			args[i] = NULL;
			cantArgs--;
			flagstatus=1;
			return i;
		}
	}
	
	for (i=0;i<cantArgs;i++){
		if(strcmp(args[i],"<")==0){
			args[i] = NULL;
			cantArgs--;
			flagstatus=2;
			return i;
		}
	}
	
	for (i=0;i<cantArgs;i++){
		if(strcmp(args[i],">")==0){
			args[i] = NULL;
			cantArgs--;
			flagstatus=3;
			return i;
		}
	}
	
	for (i=0;i<cantArgs;i++){
		if(strcmp(args[i],"|")==0){
			args[i] = NULL;
			cantArgs--;
			flagstatus=4;
			return i;
		}
	}
	return i;
}
//Muestra el prompt
void showPrompt(){
    printf("%s # %s > ", getenv("LOGNAME"),(char*) getcwd(NULL, 0));            
}

//Parsea una linea
int parsing(char line[], char* args[], char type1[], char type2[]){ 
    int i = 0;
    args[i] = strtok(line, type1);    
    while((args [i]) != NULL) {
		i++;
        args[i] = strtok (NULL, type2);
    }
    return i;
}

//Interpreta el comando: si es cd llama a changedir(), si no es cd llama a seek().
void command(char* args[], char* path){
    if(strcmp(args[0], "exit\n") == 0) exit(1);
    if(strcmp(args[0], "\n") == 0) return;
    if(strcmp(args[0], "cd") == 0) changeDir(args, path);
	else seek(args);
}

//Cambia el directorio (comando cd)
void changeDir(char* args[], char* path){
    if( chdir(args[1]) != 0 ) perror("Command not found\n");
	else path = (char*) getcwd(NULL, 0);
}

//Busqueda de secuencia PATH, caminos abusolutos y relativos 
void seek(char *args[]){
    char str[200];
    int flag = 0;
    int num = 0;
    pid_t pid;

    //En cada uno de los PATH debo probar si esta el comando 
    
    if ( (pid = fork()) == 0){  // HIJO ---------------------------------------------------------------------------------------
                
		if(flagstatus==2||flagstatus==3){
			redirection(args[position+1]);
			args[position+1]=NULL;
			}
		if(args[0][0]=='.'){          //Si comienza con un ./ o   ../
			char * aux1;
            char * aux2;
            int status = 0;
    
            aux1 = strtok(args[0],"/");     
            aux2 = aux1;
    
            while((status == 0)&&((aux1 = strtok(NULL,"/"))!= NULL)){
				status = chdir(aux2);   
                aux2 = aux1;            
			}                                       
    
            char* path = (char*) getcwd(NULL, 0);   //Encuentra el pwd.
            strcat (path,"/");
            strcat (path,aux2);     
            strncpy(str,path,sizeof(str));
    
			//Quita el final de linea, solo si no habia & (ya que si habia, fue quitado):
			if (flagstatus==0){              
				int j=0;
				int n=0;
				while(str[j]!='\0')j++;                
                for(n=0;n<j;n++) if((str[n]=='\n')||(str[n]=='\0'))str[n]='\0';
			}
                        
			if((access(str,F_OK))==0){
				execv(str, args);
				exit(1);
			}
			else{
				perror("Error, File not found \n");
				exit(1);
			}
		}
		else{   
			//Si no comienza con un ./ o / 
            int i = 0;        
            char* dir[100];
                
            while( directories[i] != NULL ){                    
				//Busca en PATH el comando especificado:    
                //Arma el string = path + / + comando:    
                dir[i] = directories[i];
                strcpy (str, dir[i]);                            
                strcat (str, "/");
                strcat(str, args[0]);

				//Quita el final de linea, solo si no habia & (ya que si habia, fue quitado):
                if (flagstatus==0){
					int j = 0;
                    while(str[j]!='\0')j++;                
                    if ( args[1] != NULL ) str[j] = '\0';
                    else str[j-1] = '\0';
				}
                //Si lo encuentra lo ejecuta:
                
                if( (access(str,F_OK)) == 0 ){
                execv(str, args);
                num = 1;
                }
				else i++;       //Si no lo encientra incrementa i, para buscar en otro directorio.
			}
             
             //Si no encontro el comando en todo PATH muestra un mensaje de error:
             if( num == 0 ){
				perror("File not found");
                exit(1);
			}
        }            
	}
	else if ( pid>0 )flag =-1; // PADRE ---------------------------------------------------------------------------------------
                
    if(flagstatus!=1){                                     	//Si no habia &, espera al hijo
		waitpid(pid, &flag, 0);         
        args=NULL;                      
	}
	else {                                                	//Si habia &, no lo espera
		args = NULL;
        waitpid(pid, &flag, WNOHANG);
	}
        
}

//Redireccion de I/O
void redirection(char* file_name){
	int fid;

	if(flagstatus ==2){
		fid = open(file_name, O_RDONLY, S_IWUSR|S_IRUSR);
		if(fid<0) perror("open");
		close(STDIN_FID);
		if(dup(fid)<0)perror("dup");
	}

	if(flagstatus ==3){
		fid = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
		if(fid<0) perror("open");
		close(STDOUT_FID);
		if(dup(fid)<0)perror("dup");
	}
}	

